/*
===========================================================================
ARX FATALIS GPL Source Code
Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.

This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').

Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code.  If not, see
<http://www.gnu.org/licenses/>.

In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
//////////////////////////////////////////////////////////////////////////////////////
//   @@        @@@        @@@                @@                           @@@@@     //
//   @@@       @@@@@@     @@@     @@        @@@@                         @@@  @@@   //
//   @@@       @@@@@@@    @@@    @@@@       @@@@      @@                @@@@        //
//   @@@       @@  @@@@   @@@  @@@@@       @@@@@@     @@@               @@@         //
//  @@@@@      @@  @@@@   @@@ @@@@@        @@@@@@@    @@@            @  @@@         //
//  @@@@@      @@  @@@@  @@@@@@@@         @@@@ @@@    @@@@@         @@ @@@@@@@      //
//  @@ @@@     @@  @@@@  @@@@@@@          @@@  @@@    @@@@@@        @@ @@@@         //
// @@@ @@@    @@@ @@@@   @@@@@            @@@@@@@@@   @@@@@@@      @@@ @@@@         //
// @@@ @@@@   @@@@@@@    @@@@@@           @@@  @@@@   @@@ @@@      @@@ @@@@         //
// @@@@@@@@   @@@@@      @@@@@@@@@@      @@@    @@@   @@@  @@@    @@@  @@@@@        //
// @@@  @@@@  @@@@       @@@  @@@@@@@    @@@    @@@   @@@@  @@@  @@@@  @@@@@        //
//@@@   @@@@  @@@@@      @@@      @@@@@@ @@     @@@   @@@@   @@@@@@@    @@@@@ @@@@@ //
//@@@   @@@@@ @@@@@     @@@@        @@@  @@      @@   @@@@   @@@@@@@    @@@@@@@@@   //
//@@@    @@@@ @@@@@@@   @@@@             @@      @@   @@@@    @@@@@      @@@@@      //
//@@@    @@@@ @@@@@@@   @@@@             @@      @@   @@@@    @@@@@       @@        //
//@@@    @@@  @@@ @@@@@                          @@            @@@                  //
//            @@@ @@@                           @@             @@        STUDIOS    //
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
// ARX_NPC
//////////////////////////////////////////////////////////////////////////////////////
//
// Description:
//		ARX Minimap Management
//
// Updates: (date) (person) (update)
//
// Code: Cyril Meynier
//
// Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved
//////////////////////////////////////////////////////////////////////////////////////

#include "ARX_Levels.h"
#include "ARX_Minimap.h"
#include "ARX_Text.h"
#include "EERIELight.h"
#include "EERIEPhysicsBox.h"
#include "EERIEDRAW.h"
#include "EERIEObject.h"
#include "Hermesmain.h"
#include "danae.h"

#include <algorithm>
#include <cstdio>

#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>

MINI_MAP_DATA minimap[MAX_MINIMAPS];
float mini_offset_x[MAX_MINIMAPS];
float mini_offset_y[MAX_MINIMAPS];
float mapmaxy[32];

TextureContainer * pTexDetect = NULL;

extern long FOR_EXTERNAL_PEOPLE;

MAPMARKER_DATA * Mapmarkers = NULL;
long Nb_Mapmarkers = 0;


//-----------------------------------------------------------------------------
void ARX_MINIMAP_GetData(long SHOWLEVEL)
{
	if (minimap[SHOWLEVEL].tc == NULL)
	{
		char name[256];
		char LevelMap[256];
		GetLevelNameByNum(SHOWLEVEL, name);

		sprintf(LevelMap, "Graph\\Levels\\Level%s\\map.bmp", name);
		minimap[SHOWLEVEL].tc = MakeTCFromFile(LevelMap);

		if (minimap[SHOWLEVEL].tc) // 4 pix/meter
		{
			minimap[SHOWLEVEL].tc->Restore(GDevice);
			SpecialBorderSurface(minimap[SHOWLEVEL].tc, minimap[SHOWLEVEL].tc->m_dwOriginalWidth, minimap[SHOWLEVEL].tc->m_dwOriginalHeight);


			minimap[SHOWLEVEL].height = ARX_CLEAN_WARN_CAST_FLOAT(minimap[SHOWLEVEL].tc->m_dwHeight);
			minimap[SHOWLEVEL].width = ARX_CLEAN_WARN_CAST_FLOAT(minimap[SHOWLEVEL].tc->m_dwWidth);


			float minx = FLT_MAX;
			float maxx = FLT_MIN;
			float miny = FLT_MAX;
			float maxy = FLT_MIN;
			EERIEPOLY * ep;
			EERIE_BKG_INFO * eg;

			for (long j = 0; j < ACTIVEBKG->Zsize; j++)
			{
				for (long i = 0; i < ACTIVEBKG->Xsize; i++)
				{
					eg = &ACTIVEBKG->Backg[i+j*ACTIVEBKG->Xsize];

					for (long k = 0; k < eg->nbpoly; k++)
					{
						ep = &eg->polydata[k];

						if (ep)
						{
							minx = __min(minx, ep->min.x);
							maxx = __max(maxx, ep->max.x);
							miny = __min(miny, ep->min.z);
							maxy = __max(maxy, ep->max.z);
						}
					}
				}

				mapmaxy[SHOWLEVEL] = maxy;
				minimap[SHOWLEVEL].xratio = minx;
				minimap[SHOWLEVEL].yratio = miny;

				for (long iii = 0; iii < 32; iii++)
				{
					minimap[iii].offsetx = 0;
					minimap[iii].offsety = 0;
				}
			}
		}
	}
}
extern long Book_MapPage;
//-----------------------------------------------------------------------------
void ARX_MINIMAP_ValidatePos(EERIE_3D * pos)
{
	long SHOWLEVEL = ARX_LEVELS_GetRealNum(CURRENTLEVEL);

	if (SHOWLEVEL >= 0 && SHOWLEVEL < 32)
	{
		if (minimap[SHOWLEVEL].tc == NULL)
			ARX_MINIMAP_GetData(SHOWLEVEL);

		if (minimap[CURRENTLEVEL].tc == NULL)
			ARX_MINIMAP_GetData(CURRENTLEVEL);

		if (minimap[SHOWLEVEL].tc && minimap[SHOWLEVEL].tc->m_pddsSurface)
			ARX_MINIMAP_Show(GDevice, ARX_LEVELS_GetRealNum(CURRENTLEVEL), 2, true);
	}
}

float AM_LASTPOS_x = -999999.f;
float AM_LASTPOS_z = -999999.f;
void ARX_MINIMAP_ValidatePlayerPos()
{
	if (BLOCK_PLAYER_CONTROLS) return;

	float dist = Distance2D(AM_LASTPOS_x, AM_LASTPOS_z, player.pos.x, player.pos.z);
	float req;

	if ((player.Interface & INTER_MAP) && (!(player.Interface & INTER_COMBATMODE)) && (Book_Mode == BOOKMODE_MINIMAP))
		req = 20.f;
	else req = 80.f;

	if (dist > req)
	{
		AM_LASTPOS_x = player.pos.x;
		AM_LASTPOS_z = player.pos.z;
		ARX_MINIMAP_ValidatePos(&player.pos);
	}
}

//-----------------------------------------------------------------------------
// MINIMAP
//-----------------------------------------------------------------------------
void ARX_MINIMAP_Load_Offsets()
{
	char fic[256];
	sprintf(fic, "%s\\Graph\\Levels\\mini_offsets.ini", Project.workingdir);

	if (PAK_FileExist(fic))
	{
		long siz = 0;
		char * dat = (char *)PAK_FileLoadMalloc(fic, &siz);
		long pos = 0;

		if (dat)
		{
			for (long i = 0; i < 29; i++)
			{
				char t[512];
				sscanf(dat + pos, "%s %f %f", t, &mini_offset_x[i], &mini_offset_y[i]);

				while ((pos < siz) && (dat[pos] != 10)) pos++;

				pos++;

				if (pos >= siz) i = 100;
			}

			free(dat);
		}

	}

	mini_offset_x[0] = 0;
	mini_offset_y[0] = -0.5;
	mini_offset_x[1] = 0;
	mini_offset_y[1] = 0;
	mini_offset_x[14] = 130;
	mini_offset_y[14] = 0;
	mini_offset_x[15] = 31;
	mini_offset_y[15] = -3.5;
}

void ARX_MINIMAP_Reveal()
{
	for (long i = 0; i < MAX_MINIMAPS; ++i)
		for (long k = 0; k < MINIMAP_MAX_X; ++k)
			for (long j = 0; j < MINIMAP_MAX_Z; ++j)
				minimap[i].revealed[k][j] = 255;
}
//-----------------------------------------------------------------------------
void ARX_MINIMAP_FirstInit()
{
	memset(minimap, 0, sizeof(MINI_MAP_DATA)*MAX_MINIMAPS);

	for (long i = 0; i < MAX_MINIMAPS; ++i)
	{
		mini_offset_x[i] = 0.f;
		mini_offset_y[i] = 0.f;
	}

	ARX_MINIMAP_Load_Offsets();
}

//-----------------------------------------------------------------------------
void ARX_MINIMAP_Reset()
{
	for (long i = 0; i < MAX_MINIMAPS; i++)
	{
		if (minimap[i].tc)
		{
			D3DTextr_KillTexture(minimap[i].tc);
			minimap[i].tc = NULL;
		}
	}

	memset(minimap, 0, sizeof(MINI_MAP_DATA)*MAX_MINIMAPS);
}

//-----------------------------------------------------------------------------
void ARX_MINIMAP_PurgeTC()
{
	for (long i = 0; i < MAX_MINIMAPS; i++)
	{
		if (minimap[i].tc)
		{
			D3DTextr_KillTexture(minimap[i].tc);
			minimap[i].tc = NULL;
		}
	}
}

//-----------------------------------------------------------------------------



TextureContainer * MapMarkerTc = NULL;

float DECALY = -150;
float DECALX = +40;
//-----------------------------------------------------------------------------
void ARX_MINIMAP_Show(LPDIRECT3DDEVICE7 m_pd3dDevice, long SHOWLEVEL, long flag, bool detection, bool corner)
{
	// Nuky - centralized some constants and dezoomed ingame minimap
	static const int CORNER_SIZE = 300;
	static const int CORNER_LEFT = 390;
	static const int CORNER_RIGHT = 590;
	static const int CORNER_TOP = 135;
	static const int CORNER_BOTTOM = 295;
	static const float CORNER_PLAYERSIZE = 4.f;

	// First Load Minimap TC & DATA if needed
	if (!minimap[SHOWLEVEL].tc)
		ARX_MINIMAP_GetData(SHOWLEVEL);

	if (!minimap[SHOWLEVEL].tc || !minimap[SHOWLEVEL].tc->m_pddsSurface)
		return;

	float startx, starty, casex, casey, ratiooo;
	float mod_x = (float)MAX_BKGX / (float)MINIMAP_MAX_X;
	float mod_z = (float)MAX_BKGZ / (float)MINIMAP_MAX_Z;

	if (flag == 1)
	{
		const int size = corner ? CORNER_SIZE : 900;

		startx = 0;
		starty = 0;
		casex = size / (float)MINIMAP_MAX_X;
		casey = size / (float)MINIMAP_MAX_Z;
		ratiooo = size / 250.f;
	}
	else
	{
		startx = 140;
		starty = 120;
		casex = 250 / (float)MINIMAP_MAX_X;
		casey = 250 / (float)MINIMAP_MAX_Z;
		ratiooo = 1.f;
	}

	float sstartx = startx;
	float sstarty = starty;
	float ofx = mini_offset_x[CURRENTLEVEL];
	float ofx2 = minimap[SHOWLEVEL].xratio;
	float ofy = mini_offset_y[CURRENTLEVEL];
	float ofy2 = minimap[SHOWLEVEL].yratio;
	float px = 0.f;
	float py = 0.f;

	if (SHOWLEVEL == ARX_LEVELS_GetRealNum(CURRENTLEVEL) || flag == 2)
	{
		px = startx + ((player.pos.x + ofx - ofx2) * DIV100 * casex
			+ mini_offset_x[CURRENTLEVEL] * ratiooo * mod_x) / mod_x ;
		py = starty + ((mapmaxy[SHOWLEVEL] - ofy - ofy2) * DIV100 * casey
			- (player.pos.z + ofy - ofy2) * DIV100 * casey + mini_offset_y[CURRENTLEVEL] * ratiooo * mod_z) / mod_z ;

		if (flag == 1)
		{
			startx = 490.f - px;
			starty = 220.f - py;
			px = 490.f;
			py = 220.f;
		}
	}

	D3DTLVERTEX verts[4];
	SETTC(m_pd3dDevice, minimap[SHOWLEVEL].tc);

	for (long k = 0; k < 4; ++k)
	{
		verts[k].color = 0xFFFFFFFF;
		verts[k].rhw = 1;
		verts[k].sz = 0.00001f;
	}

	float div = DIV25;
	TextureContainer * tc = minimap[SHOWLEVEL].tc;
	float dw = 1.f / (float)std::max(tc->m_dwDeviceWidth, tc->m_dwOriginalWidth);
	float dh = 1.f / (float)std::max(tc->m_dwDeviceHeight, tc->m_dwOriginalHeight);

	float vx2 = 4.f * dw * mod_x;
	float vy2 = 4.f * dh * mod_z;

	RECT boundaries;
	boundaries.bottom = 0;
	boundaries.left   = 0;
	boundaries.right  = 0;
	boundaries.top    = 0;
	float MOD20 = 0.f;
	float MOD20DIV  = 0.f;
	float divXratio = 0.f;
	float divYratio = 0.f;

	if (flag != 2)
	{
		if (flag == 1)
		{
			MOD20 = 20.f * Xratio;
			MOD20DIV = 1.f / (MOD20);

			ARX_CHECK_LONG((360 + MOD20)*Xratio);
			ARX_CHECK_LONG((555 - MOD20)*Xratio);
			ARX_CHECK_LONG((85 + MOD20)*Yratio);
			ARX_CHECK_LONG((355 - MOD20)*Yratio);
			boundaries.left		=	ARX_CLEAN_WARN_CAST_LONG((360 + MOD20) * Xratio);
			boundaries.right	=	ARX_CLEAN_WARN_CAST_LONG((555 - MOD20) * Xratio);
			boundaries.top		=	ARX_CLEAN_WARN_CAST_LONG((85 + MOD20) * Yratio);
			boundaries.bottom	=	ARX_CLEAN_WARN_CAST_LONG((355 - MOD20) * Yratio);

			if (corner)
			{
				ARX_CHECK_LONG((CORNER_LEFT + MOD20)*Xratio);
				ARX_CHECK_LONG((CORNER_RIGHT - MOD20)*Xratio);
				ARX_CHECK_LONG((CORNER_TOP + MOD20)*Yratio);
				ARX_CHECK_LONG((CORNER_BOTTOM - MOD20)*Yratio);
				boundaries.left		=	ARX_CLEAN_WARN_CAST_LONG((CORNER_LEFT + MOD20) * Xratio);
				boundaries.right	=	ARX_CLEAN_WARN_CAST_LONG((CORNER_RIGHT - MOD20) * Xratio);
				boundaries.top		=	ARX_CLEAN_WARN_CAST_LONG((CORNER_TOP + MOD20) * Yratio);
				boundaries.bottom	=	ARX_CLEAN_WARN_CAST_LONG((CORNER_BOTTOM - MOD20) * Yratio);
			}
		}

		SETALPHABLEND(m_pd3dDevice, TRUE);
		m_pd3dDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,  D3DBLEND_ZERO);
		m_pd3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCCOLOR);
		m_pd3dDevice->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_ALWAYS);
		SETTEXTUREWRAPMODE(m_pd3dDevice, D3DTADDRESS_CLAMP);

		if (corner)
		{
			m_pd3dDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,  D3DBLEND_ONE);
			m_pd3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCCOLOR);
		}
	}
	else
	{
		divXratio = 1.f / Xratio;
		divYratio = 1.f / Yratio;
	}

	for (long j = -2; j < MINIMAP_MAX_Z + 2; j++)
	{
		for (long i = -2; i < MINIMAP_MAX_X + 2; i++)
		{
			float vx, vy, vxx, vyy;
			vxx = ((float)i * (float)ACTIVEBKG->Xdiv * mod_x);
			vyy = ((float)j * (float)ACTIVEBKG->Zdiv * mod_z);
			vx = (vxx * div) * dw;
			vy = (vyy * div) * dh;

			long okay = 1;
			float posx = (startx + i * casex) * Xratio;
			float posy = (starty + j * casey) * Yratio;

			if (flag == 1)
			{

				if	((posx < 360 * Xratio)
					||	(posx > 555 * Xratio)
					||	(posy < 85 * Yratio)
					||	(posy > 355 * Yratio))
					okay = 0;

				if (corner)
				{
					okay = 1;

					if	((posx < CORNER_LEFT * Xratio)
						||	(posx > CORNER_RIGHT * Xratio)
						||	(posy < CORNER_TOP * Yratio)
						||	(posy > CORNER_BOTTOM * Yratio))
						okay = 0;
				}

			}
			else
			{
				if ((posx > 345 * Xratio)
					||	(posy > 290 * Yratio))
					okay = 0;
			}

			if (okay)
			{
				if ((flag == 2)
					&& (i >= 0) && (i < MINIMAP_MAX_X)
					&& (j >= 0) && (j < MINIMAP_MAX_Z))
				{
					float d = Distance2D(posx * divXratio + casex * DIV2, posy * divYratio /*-casey * 2 * Yratio*/, px, py);

					if (d <= 6.f)
					{
						long r;
						float vv = (6 - d) * DIV6;

						if (vv >= 0.5f)
							vv = 1.f;
						else if (vv > 0.f)
							vv = vv * 2.f;
						else
							vv = 0.f;

						F2L((float)(vv * 255.f), &r);


						long ucLevel =  __max(r, minimap[SHOWLEVEL].revealed[i][j]);
						ARX_CHECK_UCHAR(ucLevel);

						minimap[SHOWLEVEL].revealed[i][j] = ARX_CLEAN_WARN_CAST_UCHAR(ucLevel);


					}
				}

				if (!FOR_EXTERNAL_PEOPLE)
				{
					if (0 <= i && i < MINIMAP_MAX_X && 0 <= j && j < MINIMAP_MAX_Z)
						minimap[SHOWLEVEL].revealed[i][j] = 255;
				}

				verts[3].sx = verts[0].sx = (posx);
				verts[1].sy = verts[0].sy = (posy);
				verts[2].sx = verts[1].sx = posx + (casex * Xratio);
				verts[3].sy = verts[2].sy = posy + (casey * Yratio);

				verts[3].tu = verts[0].tu = vx;
				verts[1].tv = verts[0].tv = vy;
				verts[2].tu = verts[1].tu = vx + vx2;
				verts[3].tv = verts[2].tv = vy + vy2;

				if (flag != 2)
				{
					float _px;
					float v;
					float oo = 0.f;

					if (i < 0 || i >= MINIMAP_MAX_X || j < 0 || j >= MINIMAP_MAX_Z)
						v = 0;
					else
						v = minimap[SHOWLEVEL].revealed[i][j] * DIV255;

					if (flag == 1)
					{
						_px = verts[0].sx - boundaries.left;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = boundaries.right - verts[0].sx;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = verts[0].sy - boundaries.top;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = boundaries.bottom - verts[0].sy;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;
					}

					if (corner)
						verts[0].color = D3DRGB(v * DIV2, v * DIV2, v * DIV2);
					else
						verts[0].color = D3DRGB(v, v, v);

					oo += v;

					if (i + 1 < 0 || i + 1 >= MINIMAP_MAX_X || j < 0 || j >= MINIMAP_MAX_Z)
						v = 0;
					else
						v = minimap[SHOWLEVEL].revealed[__min(i+1, MINIMAP_MAX_X-1)][j] * DIV255;

					if (flag == 1)
					{
						_px = verts[1].sx - boundaries.left;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = boundaries.right - verts[1].sx;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = verts[1].sy - boundaries.top;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = boundaries.bottom - verts[1].sy;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;
					}

					if (corner)
						verts[1].color = D3DRGB(v * DIV2, v * DIV2, v * DIV2);
					else
						verts[1].color = D3DRGB(v, v, v);

					oo += v;

					if (i + 1 < 0 || i + 1 >= MINIMAP_MAX_X || j + 1 < 0 || j + 1 >= MINIMAP_MAX_Z)
						v = 0;
					else
						v = minimap[SHOWLEVEL].revealed[__min(i+1, MINIMAP_MAX_X-1)][__min(j+1, MINIMAP_MAX_Z-1)] * DIV255;

					if (flag == 1)
					{
						_px = verts[2].sx - boundaries.left;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = boundaries.right - verts[2].sx;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = verts[2].sy - boundaries.top;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = boundaries.bottom - verts[2].sy;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;
					}

					if (corner)
						verts[2].color = D3DRGB(v * DIV2, v * DIV2, v * DIV2);
					else
						verts[2].color = D3DRGB(v, v, v);

					oo += v;

					if (i < 0 || i >= MINIMAP_MAX_X || j + 1 < 0 || j + 1 >= MINIMAP_MAX_Z)
						v = 0;
					else
						v = minimap[SHOWLEVEL].revealed[i][__min(j+1, MINIMAP_MAX_Z-1)] * DIV255;

					if (flag == 1)
					{
						_px = verts[3].sx - boundaries.left;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = boundaries.right - verts[3].sx;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = verts[3].sy - boundaries.top;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;

						_px = boundaries.bottom - verts[3].sy;

						if (_px < 0.f) v = 0.f;
						else if (_px < MOD20) v *= _px * MOD20DIV;
					}

					if (corner)
						verts[3].color = D3DRGB(v * DIV2, v * DIV2, v * DIV2);
					else
						verts[3].color = D3DRGB(v, v, v);

					oo += v;

					if (oo > 0.f)
					{
						if (corner)
						{
							verts[0].sx += DECALX * Xratio;
							verts[0].sy += DECALY * Yratio;
							verts[1].sx += DECALX * Xratio;
							verts[1].sy += DECALY * Yratio;
							verts[2].sx += DECALX * Xratio;
							verts[2].sy += DECALY * Yratio;
							verts[3].sx += DECALX * Xratio;
							verts[3].sy += DECALY * Yratio;
						}

						EERIEDRAWPRIM(GDevice, D3DPT_TRIANGLEFAN, D3DFVF_TLVERTEX | D3DFVF_DIFFUSE, verts, 4, 0);
					}
				}
			}
		}
	}

	if (flag != 2)
	{
		m_pd3dDevice->SetTextureStageState(0, D3DTSS_ADDRESS, D3DTADDRESS_WRAP);
		m_pd3dDevice->SetRenderState(D3DRENDERSTATE_ZFUNC, D3DCMP_LESSEQUAL);

		SETALPHABLEND(m_pd3dDevice, FALSE);

		if ((SHOWLEVEL == ARX_LEVELS_GetRealNum(CURRENTLEVEL)))
		{
			// Now Draws Playerpos/angle
			verts[0].color = 0xFFFF0000;
			verts[1].color = 0xFFFF0000;
			verts[2].color = 0xFFFF0000;

			float val = flag == 1 ? 6.f : 3.f;
			if (corner)
				val = CORNER_PLAYERSIZE;

			float rx = 0.f;
			float ry = -val * 1.8f;
			float rx2 = -val * DIV2;
			float ry2 = val;
			float rx3 = val * DIV2;
			float ry3 = val;

			float angle = DEG2RAD(player.angle.b);
			float ca = EEcos(angle);
			float sa = EEsin(angle);

			verts[0].sx = (px + rx2 * ca + ry2 * sa) * Xratio;
			verts[0].sy = (py + ry2 * ca - rx2 * sa) * Yratio;
			verts[1].sx = (px + rx * ca + ry * sa) * Xratio;
			verts[1].sy = (py + ry * ca - rx * sa) * Yratio;
			verts[2].sx = (px + rx3 * ca + ry3 * sa) * Xratio;
			verts[2].sy = (py + ry3 * ca - rx3 * sa) * Yratio;

			SETTC(GDevice, NULL);

			if (corner)
			{
				SETALPHABLEND(m_pd3dDevice, TRUE);
				verts[0].sx += DECALX * Xratio;
				verts[0].sy += DECALY * Yratio;
				verts[1].sx += DECALX * Xratio;
				verts[1].sy += DECALY * Yratio;
				verts[2].sx += DECALX * Xratio;
				verts[2].sy += DECALY * Yratio;
			}

			EERIEDRAWPRIM(GDevice, D3DPT_TRIANGLEFAN, D3DFVF_TLVERTEX | D3DFVF_DIFFUSE, verts, 3, 0);

			if (corner)
				SETALPHABLEND(m_pd3dDevice, FALSE);
		}
	}

	// Red detection dots
	if (detection)
	{
		if (!pTexDetect)
			pTexDetect = GetTextureFile("Graph\\particles\\flare.bmp");

		SETALPHABLEND(m_pd3dDevice, true);
		if (!corner)
		{
			m_pd3dDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND,  D3DBLEND_ONE);
			m_pd3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_ONE);
		}

		for (long i = 1; i < inter.nbmax; i++)
		{
			if (INTERACTIVE_OBJ* iobj = inter.iobj[i])
			{
				if ((inter.iobj[i]->ioflags & IO_NPC) && iobj->_npcdata->life > 0.f &&		// Alive NPC
					!(iobj->GameFlags & GFLAG_MEGAHIDE) &&									// Not hidden
					iobj->show == SHOW_FLAG_IN_SCENE &&										// To be shown
					inter.iobj[i]->_npcdata->fDetect >= 0.f &&								// Can be detected
					player.Full_Skill_Etheral_Link >= inter.iobj[i]->_npcdata->fDetect)		// is detected?
				{
					float fpx = flag == 1 ? startx : sstartx;
					float fpy = flag == 1 ? starty : sstarty;

					fpx += ((iobj->pos.x - 100 + ofx - ofx2) * DIV100 * casex
						+ mini_offset_x[CURRENTLEVEL] * ratiooo * mod_x) / mod_x;
					fpy += ((mapmaxy[SHOWLEVEL] - ofy - ofy2) * DIV100 * casey
						- (iobj->pos.z + 200 + ofy - ofy2) * DIV100 * casey + mini_offset_y[CURRENTLEVEL] * ratiooo * mod_z) / mod_z;

					float d = Distance2D(player.pos.x, player.pos.z, iobj->pos.x, iobj->pos.z);

					if (d <= 800 && fabs(inter.iobj[0]->pos.y - iobj->pos.y) < 250.f)
					{
						float col = d > 600.f ? 1.f - (d - 600.f) * DIV200 : 1.f;

						if (corner)
						{
							fpx += DECALX * Xratio;
							fpy += (DECALY + 15) * Yratio;
						}

						fpx *= Xratio;
						fpy *= Yratio;
						EERIEDrawBitmap(GDevice, fpx, fpy, 5.f * ratiooo, 5.f * ratiooo, 0, pTexDetect, D3DRGB(col, 0, 0));
					}
				}
			}
		}

		if (!corner)
			SETALPHABLEND(m_pd3dDevice, false);
	}

	if (flag == 0)
		for (long i = 0; i < Nb_Mapmarkers; i++)
		{
			if (Mapmarkers[i].lvl == SHOWLEVEL + 1)
			{
				float pos_x = Mapmarkers[i].x * 8 * ratiooo * ACTIVEBKG->Xmul * casex + startx;
				float pos_y = Mapmarkers[i].y * 8 * ratiooo * ACTIVEBKG->Zmul * casey + starty;
				float size = 5.f * ratiooo;
				verts[0].color = 0xFFFF0000;
				verts[1].color = 0xFFFF0000;
				verts[2].color = 0xFFFF0000;
				verts[3].color = 0xFFFF0000;
				verts[0].sx = (pos_x - size) * Xratio;
				verts[0].sy = (pos_y - size) * Yratio;
				verts[1].sx = (pos_x + size) * Xratio;
				verts[1].sy = (pos_y - size) * Yratio;
				verts[2].sx = (pos_x + size) * Xratio;
				verts[2].sy = (pos_y + size) * Yratio;
				verts[3].sx = (pos_x - size) * Xratio;
				verts[3].sy = (pos_y + size) * Yratio;
				verts[0].tu = 0.f;
				verts[0].tv = 0.f;
				verts[1].tu = 1.f;
				verts[1].tv = 0.f;
				verts[2].tu = 1.f;
				verts[2].tv = 1.f;
				verts[3].tu = 0.f;
				verts[3].tv = 1.f;

				if (!corner && MouseInRect(verts[0].sx, verts[0].sy, verts[2].sx, verts[2].sy))
				{
					if (!Mapmarkers[i].tstring)
					{
						_TCHAR output[4096];
						MakeLocalised(Mapmarkers[i].string, output, 4096, 0);
						Mapmarkers[i].tstring = (_TCHAR *)malloc((_tcslen(output) + 1) * sizeof(_TCHAR));
						ZeroMemory(Mapmarkers[i].tstring, (_tcslen(output) + 1)*sizeof(_TCHAR));
						_tcscpy(Mapmarkers[i].tstring, output);
					}

					if (Mapmarkers[i].tstring)
					{
						static const float marker_tooltip_left = 140;
						static const float marker_tooltip_top = 290;
						static const float marker_tooltip_right = 140 + 205;
						static const float marker_tooltip_bottom = 358;

						RECT rRect;
						rRect.left		= static_cast<long>(marker_tooltip_left * Xratio);
						rRect.right		= static_cast<long>(marker_tooltip_right * Xratio);
						rRect.top		= static_cast<long>(marker_tooltip_top * Yratio);
						rRect.bottom	= static_cast<long>(marker_tooltip_bottom * Yratio);

						long lLengthDraw = ARX_UNICODE_ForceFormattingInRect(hFontInGameNote, Mapmarkers[i].tstring, 0, rRect);

						danaeApp.DANAEEndRender();
						_TCHAR Page_Buffer[256];
						_tcsncpy(Page_Buffer, Mapmarkers[i].tstring, lLengthDraw);
						Page_Buffer[lLengthDraw] = _T('\0');
						DrawBookTextInRect(hFontInGameNote, marker_tooltip_left, marker_tooltip_top,
										   marker_tooltip_right, marker_tooltip_bottom, Page_Buffer, 0, 0x00FF00FF);
						danaeApp.DANAEStartRender();
					}
				}

				if (MapMarkerTc == NULL)
					MapMarkerTc = MakeTCFromFile("Graph\\interface\\icons\\mapmarker.bmp");

				SETTC(GDevice, MapMarkerTc);

				if (corner)
				{
					verts[0].sx += DECALX * Xratio;
					verts[0].sy += DECALY * Yratio;
					verts[1].sx += DECALX * Xratio;
					verts[1].sy += DECALY * Yratio;
					verts[2].sx += DECALX * Xratio;
					verts[2].sy += DECALY * Yratio;
					verts[3].sx += DECALX * Xratio;
					verts[3].sy += DECALY * Yratio;
				}

				EERIEDRAWPRIM(GDevice, D3DPT_TRIANGLEFAN, D3DFVF_TLVERTEX | D3DFVF_DIFFUSE, verts, 4, 0);
			}
		}
}

void ARX_MAPMARKER_Init()
{
	if (Mapmarkers)
	{
		for (long i = 0; i < Nb_Mapmarkers; i++)
			free(Mapmarkers[i].tstring);
		free(Mapmarkers);
	}

	Mapmarkers = NULL;
	Nb_Mapmarkers = 0;
}
long ARX_MAPMARKER_Get(const char* str)
{
	for (long i = 0; i < Nb_Mapmarkers; i++)
		if (!stricmp(Mapmarkers[i].string, str))
			return i;

	return -1;
}
void ARX_MAPMARKER_Add(float x, float y, long lvl, const char* temp)
{
	long num = ARX_MAPMARKER_Get(temp);

	if (num >= 0) // already exists
	{
		Mapmarkers[num].lvl = lvl;
		Mapmarkers[num].x = x;
		Mapmarkers[num].y = y;

		if (Mapmarkers[num].tstring)
			free(Mapmarkers[num].tstring);

		Mapmarkers[num].tstring = NULL;
		strcpy(Mapmarkers[num].string, temp);
		return;
	}

	Mapmarkers = (MAPMARKER_DATA *)realloc(Mapmarkers, sizeof(MAPMARKER_DATA) * (Nb_Mapmarkers + 1));

	// Memory Error Handling
	if (!Mapmarkers)
	{
		Nb_Mapmarkers = 0;
		return;
	}

	Mapmarkers[Nb_Mapmarkers].lvl = lvl;
	Mapmarkers[Nb_Mapmarkers].x = x;
	Mapmarkers[Nb_Mapmarkers].y = y;
	Mapmarkers[Nb_Mapmarkers].tstring = NULL;
	strcpy(Mapmarkers[Nb_Mapmarkers].string, temp);
	Nb_Mapmarkers++;
}
void ARX_MAPMARKER_Remove(const char* temp)
{
	long num = ARX_MAPMARKER_Get(temp);

	if (num < 0) return; // Doesn't exists

	if (Nb_Mapmarkers <= 1)
	{
		ARX_MAPMARKER_Init();
		return;
	}

	if (Mapmarkers[num].tstring)
		free(Mapmarkers[num].tstring);

	Mapmarkers[num].tstring = NULL;

	for (long i = num; i < Nb_Mapmarkers - 1; i++)
		memcpy(&Mapmarkers[i], &Mapmarkers[i+1], sizeof(MAPMARKER_DATA));

	Mapmarkers = (MAPMARKER_DATA *)realloc(Mapmarkers, sizeof(MAPMARKER_DATA) * (Nb_Mapmarkers - 1));
	Nb_Mapmarkers--;
}
